using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

using Patterns.Tools.CodeGenerators.GoogleCode.Assemblies;

namespace Patterns.Tools.CodeGenerators.GoogleCode
{
	public class Scanner : MarshalByRefObject
	{
		public ScanResults[] ScanAssembly(ScanRequest request)
		{
			Console.WriteLine(@"Scanning {0}...", Path.GetFileName(request.BinaryPath));
			Console.WriteLine();

			Assembly binary = Assembly.LoadFile(request.BinaryPath);
			XElement xml = XElement.Load(request.XmlPath);

			Type[] types = binary.GetTypes().Where(item => item.IsPublic).ToArray();

			string[] namespaces = types.Select(item => item.Namespace).Where(value => !string.IsNullOrEmpty(value)).Distinct().ToArray();
			
			var results = new List<ScanResults>{ GetAssemblyWiki(binary, namespaces, request.WikiUri, request.SourceUri, request.SidebarName) };
			results.AddRange(GetNamespaceWikis(namespaces, types, request.WikiUri, request.SourceUri, request.SidebarName));
			results.AddRange(types.Select(type => WikiTypeEngine.Run(type, xml, request.SidebarName, request.WikiUri, request.SourceUri)));
			return results.ToArray();
		}

		private static ScanResults GetAssemblyWiki(Assembly dll, IEnumerable<string> namespaces, string wikiUri, string sourceUri, string sidebarName)
		{
			var name = dll.GetName().Name;
			string wikiName = WikiNames.Assembly(name);

			return new ScanResults
			{
				Name = wikiName,
				Content = new AssemblyTemplate
				{
					Model = new AssemblyModel
					{
						Target = name,
						BaseSourceUri = sourceUri,
						BaseWikiUri = wikiUri,
						Sidebar = sidebarName,
						WikiName = wikiName,
						Version = WikiHelper.GetAssemblyVersion(dll),
						Description = WikiHelper.GetAssemblyDescription(dll),
						Namespaces = namespaces
					}
				}.TransformText()
			};
		}

		private static IEnumerable<ScanResults> GetNamespaceWikis(string[] namespaces, IEnumerable<Type> types, string wikiUri, string sourceUri, string sidebarName)
		{
			Func<string, bool> isValid = ns1 => namespaces.Any(ns2 => StringComparer.OrdinalIgnoreCase.Equals(ns1, ns2));

			return namespaces.Select(item =>
			{
				string parentName = WikiHelper.GetParentName(item);
				return new ScanResults
				{
					Name = WikiNames.Namespace(item),
					ParentName = WikiHelper.GetParentWikiName(isValid(parentName), parentName),
					Content = new GoogleWikiNamespace
					{
						Name = item,
						SidebarName = sidebarName,
						WikiUri = wikiUri,
						SourceUri = sourceUri,
						Types = types.Where(t => StringComparer.OrdinalIgnoreCase.Equals(t.Namespace, item)),
						SubNamespaces = namespaces.Where(ns => ns.Length > item.Length && ns.StartsWith(item)),
						IsNamespaceValid = isValid
					}.TransformText()
				};
			});
		}
	}
}